#pragma once

#include "znx_conn.h"
#include "znx_event_loop.h"
#include "comm/znx_defs.h"
#include "comm/znx_inet.h"


///////////////// tcp listener ////////////////

typedef struct znx_tcp_listener_s znx_tcp_listener_t;

struct znx_tcp_listener_s {
    const char              *listen_addr;
    int                     backlog;
    unsigned                nodelay:1;
    unsigned                quickack:1;
    unsigned                reuseport:1;
    unsigned                reuseaddr:1;
    unsigned                ipv6only:1;

    int                     sockfd;
    znx_address_t           address;
    znx_event_handler_t     accept_handler;

    znx_tcp_listener_t      *next;
};

extern znx_tcp_listener_t g_tcp_listener_list;


static inline void
znx_tcp_listener_init(znx_tcp_listener_t *listener)
{
    listener->sockfd = INVALID_FD;
}


// listener must be register before znx_tcp_accepter_start.
static inline void
znx_tcp_listener_register(znx_tcp_listener_t *listener)
{
    listener->next = g_tcp_listener_list.next;
    g_tcp_listener_list.next = listener;
}

znx_bool_t znx_tcp_listeners_open();

void znx_tcp_listeners_close();


////////////////// tcp accepter /////////////////

typedef struct {
    int                         nthreads;
    znx_event_loop_data_t       *datas;
    znx_thread_base_t           *threads;
} znx_tcp_accepter_t;

extern znx_tcp_accepter_t       g_tcp_accepter;

void znx_tcp_accepter_start(int nthreads);

void znx_tcp_accepter_stop();

extern char *znx_tcp_accept_thread_tag;

static inline znx_bool_t
current_thread_is_tcp_accepter_thread()
{
    if (current_thread_base == NULL) {
        return ZNX_FALSE;
    }

    if (current_thread_base->tag != znx_tcp_accept_thread_tag) {
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


//////////////// tcp connect ////////////////////

typedef struct {
    unsigned                nodelay:1;
    unsigned                quickack:1;
} znx_tcp_connect_opt_t;


typedef void (*znx_tcp_connect_handler_pt)(void *data,
    znx_conn_t *conn, znx_result_t res);

void znx_tcp_connect_setup(
    znx_address_t *address,
    znx_address_t *bind_address,
    int64_t timeout_ms,
    znx_tcp_connect_opt_t *opts,
    znx_tcp_connect_handler_pt connect_handler,
    void *data);
